--- sys/kern/kern_descrip.c	Fri Dec 14 13:26:24 2001
+++ sys/kern/kern_descrip.c	Mon Apr 22 16:38:20 2002
@@ -50,6 +50,7 @@
 #include <sys/sysctl.h>
 #include <sys/vnode.h>
 #include <sys/proc.h>
+#include <sys/namei.h>
 #include <sys/file.h>
 #include <sys/stat.h>
 #include <sys/filio.h>
@@ -1183,6 +1184,63 @@
 	}
 	while (fdp->fd_lastfile > 0 && fdp->fd_ofiles[fdp->fd_lastfile] == NULL)
 		fdp->fd_lastfile--;
+}
+
+/*
+ * It is unsafe for set[ug]id processes to be started with file
+ * descriptors 0..2 closed, as these descriptors are given implicit
+ * significance in the Standard C library.  fdcheckstd() will create a
+ * descriptor referencing /dev/null for each of stdin, stdout, and
+ * stderr that is not already open.
+ */
+int
+fdcheckstd(p)
+       struct proc *p;
+{
+       struct nameidata nd;
+       struct filedesc *fdp;
+       struct file *fp;
+       register_t retval;
+       int fd, i, error, flags, devnull;
+
+       fdp = p->p_fd;
+       if (fdp == NULL)
+               return (0);
+       devnull = -1;
+       error = 0;
+       for (i = 0; i < 3; i++) {
+               if (fdp->fd_ofiles[i] != NULL)
+                       continue;
+               if (devnull < 0) {
+                       error = falloc(p, &fp, &fd);
+                       if (error != 0)
+                               break;
+                       NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, "/dev/null",
+                           p);
+                       flags = FREAD | FWRITE;
+                       error = vn_open(&nd, flags, 0);
+                       if (error != 0) {
+                               fdp->fd_ofiles[i] = NULL;
+                               fdrop(fp, p);
+                               break;
+                       }
+                       NDFREE(&nd, NDF_ONLY_PNBUF);
+                       fp->f_data = (caddr_t)nd.ni_vp;
+                       fp->f_flag = flags;
+                       fp->f_ops = &vnops;
+                       fp->f_type = DTYPE_VNODE;
+                       VOP_UNLOCK(nd.ni_vp, 0, p);
+                       devnull = fd;
+               } else {
+                       error = fdalloc(p, 0, &fd);
+                       if (error != 0)
+                               break;
+                       error = do_dup(fdp, devnull, fd, &retval, p);
+                       if (error != 0)
+                               break;
+               }
+       }
+       return (error);
 }
 
 /*
diff --exclude=CVS -ruN sys/kern/kern_exec.c sys/kern/kern_exec.c
--- sys/kern/kern_exec.c	Tue Jan 22 11:22:59 2002
+++ sys/kern/kern_exec.c	Mon Jul 29 22:21:49 2002
@@ -328,6 +328,12 @@
 				vrele(vtmp);
 			}
 		}
+		/* Close any file descriptors 0..2 that reference procfs */
+		setugidsafety(p);
+		/* Make sure file descriptors 0..2 are in use. */
+		error = fdcheckstd(p);
+		if (error != 0)
+			goto exec_fail_dealloc;
 		/*
 		 * Set the new credentials.
 		 */
@@ -336,7 +342,6 @@
 			change_euid(p, attr.va_uid);
 		if (attr.va_mode & VSGID)
 			p->p_ucred->cr_gid = attr.va_gid;
-		setugidsafety(p);
 	} else {
 		if (p->p_ucred->cr_uid == p->p_cred->p_ruid &&
 		    p->p_ucred->cr_gid == p->p_cred->p_rgid)
diff --exclude=CVS -ruN sys/sys/filedesc.h sys/sys/filedesc.h
--- sys/sys/filedesc.h	Sat Nov 25 20:30:08 2000
+++ sys/sys/filedesc.h	Sun Apr 21 08:08:57 2002
@@ -142,6 +142,7 @@
 void	fdfree __P((struct proc *p));
 int	closef __P((struct file *fp,struct proc *p));
 void	fdcloseexec __P((struct proc *p));
+int	fdcheckstd __P((struct proc *p));
 struct	file *holdfp __P((struct filedesc* fdp, int fd, int flag));
 int	getvnode __P((struct filedesc *fdp, int fd, struct file **fpp));
 int	fdissequential __P((struct file *));
